refactor(errors): route git-plumbing failure bails through CommandError#3474
Merged
Conversation
Eight sites turned a captured non-zero exit into bail!("…{stderr}"),
bypassing CommandError::from_failed_output — the one constructor that
normalizes \r and gives renderers the typed command/output split. Convert
them: resolve_git_common_dir, the bulk config --list read, config --unset,
config --get-regexp, merge-tree, merge-base, the untracked-diff numstat,
and llm.rs run_git_capture (which now builds the Cmd itself so it can name
the args). from_failed_output takes &[impl AsRef<str>] so Vec<String>
arg lists work.
generate_commit_message now wraps only the execute_llm_command failure in
LlmCommandFailed, mirroring generate_squash_message — a git failure while
building the prompt is not an LLM-command failure, and flattening it into
LlmCommandFailed.error dropped the typed stderr.
Rendering: wrapped failures (context + CommandError) render identically;
bare ones gain the standard 'git … failed (exit N)' header + gutter. The
wt list task-failure lines for merge-base errors drop the hand-rolled
'git merge-base failed for <sha> <sha>:' prefix and now match their
sibling lines (bare stderr), per the updated snapshots.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
worktrunk-bot
approved these changes
Jul 15, 2026
worktrunk-bot
left a comment
Collaborator
There was a problem hiding this comment.
Correct, behavior-preserving refactor — every converted path now routes failed-command display through the single from_failed_output constructor, and the direct-test coverage on each is thorough. One optional micro-nit inline; nothing blocking.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
max-sixty
added a commit
that referenced
this pull request
Jul 15, 2026
…dError (#3483) Follow-up to #3474: converts the remaining raw-output `bail!("{stderr}")` sites to typed `CommandError`, so every failed-command display goes through the one normalizing constructor (`\r` stripping, canonical `✗ <command> failed (exit N)` header + output gutter). **Sites converted:** - `step/shared.rs` (`git ls-files` in ignored-entry listing) and `step/commit.rs` (`git add` into the temp index) — the two commands-layer git sites. - The five claude/codex plugin CLI calls in `commands/config/{plugins,codex}.rs`, collapsed into one private `run_plugin_cli` helper in `commands/config/mod.rs`. Their failure snapshots now show the canonical header + stderr gutter instead of `✗ claude plugin marketplace add failed: <stderr>`. - `execute_llm_command` in `llm.rs` — its hand-rolled stderr/stdout/exit-code fallback ladder was `from_failed_output`'s job; deleted in favor of the constructor. The `LlmCommandFailed` wrappers read the detail via `ErrorExt::display_message()`, which extracts the captured output for command failures and matches the old text in the common cases; the picker summary pane guards on the error type (`format_summary_error`) — command failures surface their captured output, everything else keeps the full anyhow chain the pane always showed (folded in from review). The shell program is recorded by basename only (same rule as `render_llm_invocation`), so a no-output failure doesn't leak the Git Bash install path on Windows. **Deliberately left:** the two `gh pr list` / `glab mr list` bails in `picker/prs.rs`. Their consumer is the skim-footer warning sink where the current one-line text is the intended display, and picker text sanitization was explicitly dropped (#3177). **Behavior note** (intended, but visible): an LLM command that emits on both streams before failing now shows both in the error block (`combined_output()` joins stderr + stdout); the old ladder showed stderr only. **Testing:** three new error-path unit tests (`list_ignored_entries` / `stage_to_temp_index` via corrupt-config and bad-pathspec, `execute_llm_command` exit-code + basename assertion); the five plugin sites are covered by the existing failing-mock integration tests whose snapshots this updates; `test_generate_summary_llm_error` covers the summary-pane command-failure arm and `format_summary_error_keeps_chain_for_non_command_errors` the chain-preserving arm. > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
max-sixty
added a commit
that referenced
this pull request
Jul 15, 2026
) Step-12 drift fold for the v0.68.0 release: #3483 (the follow-up completing #3474) merged during the release PR CI window, so it ships in 0.68.0 but the changelog entry only covered the eight #3474 sites. The entry now covers the full cutover and cites #3483. > _This was written by Claude Code on behalf of max_
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #3459. Eight git-plumbing sites turned a captured non-zero exit into
bail!("…{stderr}"), bypassingCommandError::from_failed_output— the one constructor that normalizes\rand gives renderers the typed command/output split. This converts them (resolve_git_common_dir, the bulkconfig --listread,config --unset,config --get-regexp,merge-tree,merge-base, the untracked-diff numstat, andllm.rs::run_git_capture), so failed-command display routes through one constructor and themain.rsmultiline-error tripwire has nothing left to catch on these paths.from_failed_outputnow takes&[impl AsRef<str>]soVec<String>arg lists work.One structural fix rode along:
generate_commit_messagenow wraps only theexecute_llm_commandfailure inGitError::LlmCommandFailed, mirroringgenerate_squash_message— a git failure while building the prompt is not an LLM-command failure, and flattening it intoLlmCommandFailed.errordropped the typed stderr.Rendering: context-wrapped failures render byte-identically (the outside-repo snapshots pass untouched); bare ones gain the standard
git … failed (exit N)header + gutter. The only snapshot changes are twowt listtask-failure lines where merge-base errors drop the hand-rolledgit merge-base failed for <sha> <sha>:prefix and now match their sibling lines.Every converted error path has a direct test — five new ones using empirically-probed failure modes (invalid regexp pattern → exit 6, invalid key → exit 1, corrupt config → 128 for the lazily-populated bulk read and merge-tree, unborn HEAD for the numstat), plus the updated
run_git_captureunit test;resolve_git_common_dirandmerge_basewere already covered by existing snapshots.Two same-pattern sites remain in the commands layer (
step/shared.rsls-files,step/commit.rsstage-to-temp-index) — deliberately left for a follow-up along with the non-git CLIs (gh/glab, claude/codex).